/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package eu.fastl.dragster.web.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;

import eu.fastl.dragster.exception.MonitoringExceptionHandlerProvider;
import eu.fastl.dragster.model.ExecutionRecord;
import eu.fastl.dragster.model.ExecutionRecordFactoryProvider;
import eu.fastl.dragster.monitoring.IdStorageProvider;
import eu.fastl.dragster.monitoring.MonitoringServiceProvider;

/**
 * Servlet Filter for measuring Web app response time. Wire this filter into
 * your web application where you want to monitor execution times
 *
 * @author Ernst Fastl
 */
public class MonitoringFilter implements Filter {

    public static final String DEFAULT_MONITORING_TYPE = "HTTP_REQUEST";
    public static final String MONITORING_TYPE_INIT_PARAMETER_NAME = "MONITORING_TYPE";
    private String type = DEFAULT_MONITORING_TYPE;

    public void init(FilterConfig filterConfig) throws ServletException {
        String monitoringTypeParameter = filterConfig.getInitParameter(MONITORING_TYPE_INIT_PARAMETER_NAME);
        if (StringUtils.isNotBlank(monitoringTypeParameter)) {
            this.type = monitoringTypeParameter;
        }

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {
        boolean createdId = IdStorageProvider.createIdIfNecessary();

        long start = System.currentTimeMillis();
        try {
            chain.doFilter(request, response);
            ExecutionRecord executionRecord = createExecutionRecord(request, start);
            MonitoringServiceProvider.getMonitoringService().recordExecutionTime(executionRecord);
        } catch (Throwable exception) {
            ExecutionRecord executionRecord = createExecutionRecord(request, start);
            try {
                MonitoringExceptionHandlerProvider.getMonitoringExceptionHandler().handleException(exception,
                        executionRecord);
            } catch (Throwable rethrownException) {

                if (rethrownException instanceof RuntimeException) {
                    throw (RuntimeException) rethrownException;
                } else if (rethrownException instanceof IOException) {
                    throw (IOException) rethrownException;
                } else if (rethrownException instanceof ServletException) {
                    throw (ServletException) rethrownException;
                } else {
                    throw new IllegalStateException("Unexpected Exception type caught: "
                            + rethrownException.getClass().getName(), rethrownException);
                }
            }

        } finally {
            // If this Filter has created the current ID we need to reset it to
            // null in case this thread gets reused by the Servlet Container or
            // Application Server
            if (createdId) {
                IdStorageProvider.getIdStorage().setId(null);
            }
        }

    }

    private ExecutionRecord createExecutionRecord(ServletRequest request, long start) {
        long end = System.currentTimeMillis();
        long executionTime = end - start;

        String detail = "NON HTTP Request";

        // in case of an HTTP Request we want to record the Request URI as
        // detail info
        if (request instanceof HttpServletRequest) {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            detail = httpServletRequest.getRequestURI();
            String queryString = httpServletRequest.getQueryString();
            if (StringUtils.isNotBlank(queryString)) {
                detail = detail + "?" + queryString;
            }
        }

        ExecutionRecord executionRecord = ExecutionRecordFactoryProvider.getExecutionRecordFactory().create(
                executionTime, IdStorageProvider.getId(), this.type, detail);
        return executionRecord;
    }

    public void destroy() {
    }

}
